<?php

class Validator {

	private $rules;

	/*
	 * $rules should be a array of validate config:
	 *  [
	 *      'name' => ['stringLength', 1, 20],
	 *      'phone' => ['mobile'],
	 *      'age' => ['number', 18, 50],
	 *      'area' => true, // true for just required (not empty validate)
	 *  ]
	 */
	public function __construct(Array $rules) {
		$this->rules = $rules;
	}

	public function check(Array $data) {
		$errors = array();
		foreach ($this->rules as $key => $rule) {
			$value = isset($data[$key]) ? $data[$key] : null;
			if (is_array($rule)) {
				if (!call_user_func_array(array('self', $rule[0]), array_merge(array($value), array_slice($rule, 1)))) {
					$errors[$key] = $rule[0]; //todo: implement detail error messages. by zhaojun
				}
			} elseif ($rule === true) {
				if (empty($value)) $errors[$key] = 'exist';
			} else {
				throw new Exception('Invalid Rule: ' . strval($rule));
			}
		}
		if ($errors) throw new ValidateException($errors);
	}

	public static function number($value, $lower = null, $upper = null, $required = true) {
		return (!$required && is_null($value)) ||
			(is_numeric($value)
			&& (is_null($lower) ? true : $value >= $lower)
			&& (is_null($upper) ? true : $value <= $upper));
	}

	public static function mobile($value, $required = true) {
		return (!$required && is_null($value)) ||
				preg_match('/^1(3|4|5|6|8)\d{9}$/', $value);
	}

	public static function stringLength($value, $lower = null, $upper = null) {
		return self::number(mb_strlen($value, 'utf8'), $lower, $upper);
	}

	public static function email($value, $required = true){
		return (!$required && is_null($value)) ||
			preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!\.)){0,61}[a-zA-Z0-9_-]?\.)+[a-zA-Z0-9_](?:[a-zA-Z0-9_\-](?!$)){0,61}[a-zA-Z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $value);
	}

}

class ValidateException extends Exception {
	private $errors;
	public function __construct($errors) {
		$this->errors = $errors;
		parent::__construct('Validation fail!');
	}

	public function errors() {
		return $this->errors;
	}
}